(* ===================================================================== *)
(** TRAITEMENT : module integrant, fonctions de traitement de la sortie 
    @author Mathieu Barbin, Ocan SANKUR
    @version 1.0.* *)
(* ===================================================================== *)

(* ===================================================================== *)
(*
   Pour Ajouter un traitement :

   1) Ajouter une valeur dans le type traitement_type
   2) Ajouter les liens souhaites dans l'association table_traitement
   3) Ecrire la classe implementant le traitement souhaite, qui derive
   de la classe virtuelle traitement_seq
   4) Ajouter une classe dans la fonction d'aiguillage d'instanciation
   de nouveau traitement (en bas de ce fichier)

   Attention, les options -c, -o, -v, -x sont deja reservees dans bopsimu
*)
(* ===================================================================== *)

open Graphics

(* ===================================================================== *)
(** Tous les types primitifs de traitement. Le type pipe permet d'aller 
    plus loin. On parle de PipeN quand il est non redondant (on ne reimprime
    que si les bits ont change (ne fonctionne pas avec les bpio) *)
(* ===================================================================== *)
type traitement_type = Montre | Standard | Pipe | PipeIO | PipeN | Latex
(* ===================================================================== *)

(* ===================================================================== *)
(** Faire le lien entre l'option demandee (string) et le type de traitement *)
(* ===================================================================== *)
let table_traitement =
[
("-m", Montre);
("-montre", Montre);
("-latex", Latex);
("-tex", Latex);
("-t", Latex);
("-p", Pipe);
("-pipe", Pipe);
("-pi", PipeIO);
("-pipeI", PipeIO);
("-pipen", PipeN);
("-pn", PipeN);
("-pN", PipeN);
("-pipeN", PipeN);
("-term", Standard)
]
(* ===================================================================== *)

(* ===================================================================== *)
(** Classe virtuelle : Objet de type traitement sequentielle. 
init : pour initialiser (exemple : ouvir tous les fichiers)
quit : pour quitter (exemple fermer tous les fichiers)
traite : realise un cycle de lecture des entrees, et traite
les entrees - sorties du bloc principal du circuit.*)
(* ===================================================================== *)
class virtual traitement_seq tab_entrees tab_sorties = 
object
  method virtual get_nom_entrees : string array
  method virtual get_nom_sorties : string array
  method virtual init : unit
  method virtual quit : unit
  method virtual traite : bool array -> bool array -> unit
end
(* ===================================================================== *)

(* ===================================================================== *)
(** transtypage des bool vers les int : true donne 1 et false donne 0 *)
(* ===================================================================== *)
let int_of_bool = function
    true -> 1
  | false -> 0
(* ===================================================================== *)

(* ===================================================================== *)
(** Traitement le plus classique : affichage sequentiel des resultats,
    avec une presentation minimaliste dans le terminal.*) 
(* ===================================================================== *)
class traitement_default tab_entrees tab_sorties = 
object 
  inherit traitement_seq tab_entrees tab_sorties
  val mutable index_cycle = 0
  val nb_entrees = Array.length tab_entrees 
  val nb_sorties = Array.length tab_sorties
  val nom_entrees = Array.copy tab_entrees
  val nom_sorties = Array.copy tab_sorties
  (* methodes *)

  method get_nom_entrees = nom_entrees

  method get_nom_sorties = nom_sorties

  method init = 
    begin
      index_cycle <- 0;
      Printf.printf "%8s | " "Cycle";
      for i = 0 to pred nb_entrees do
	Printf.printf "%s " nom_entrees.(i);
      done;
      Printf.printf "| ";
      for i = 0 to pred nb_sorties do
	Printf.printf "%s " nom_sorties.(i);
      done;
      Printf.printf "\n";
      flush stdout
    end

  method traite etat_entree etat_sortie =
    begin
      Printf.printf "%8d | " index_cycle;
      (* affichage des valeurs des entrees *)
      for i = 0 to pred nb_entrees do
	Printf.printf "%d " (int_of_bool etat_entree.(i));
      done;
      Printf.printf "| "; 
      (* affichage des valeurs des sorties *)
      for i = 0 to pred nb_sorties do
	Printf.printf "%d " (int_of_bool etat_sortie.(i));
      done;
      Printf.printf "\n";
      if index_cycle = max_int
      then index_cycle <- 0
      else index_cycle <- succ index_cycle;
      flush stdout;
    end

  method quit = ()

end
(* ===================================================================== *)

(* ===================================================================== *)
(** Traitement le plus simple pour realiser un traitement plus evolue 
    avec un pipe. Affichage sequentiel ligne par ligne des valeurs, 
    sans aucune presentation. On affiche les entrees et les sorties *) 
(* ===================================================================== *)
class traitement_pipeIO tab_entrees tab_sorties = 
object
  inherit traitement_seq tab_entrees tab_sorties
  val mutable index_cycle = 0
  val nb_entrees = Array.length tab_entrees 
  val nb_sorties = Array.length tab_sorties
  val nom_entrees = Array.copy tab_entrees
  val nom_sorties = Array.copy tab_sorties
  (* methodes *)

  method get_nom_entrees = nom_entrees

  method get_nom_sorties = nom_sorties
    
  method init =
    begin
      index_cycle <- 0;
    end

  method traite etat_entree etat_sortie =
    let not_empty = Array.length etat_entree + Array.length etat_sortie > 0 in
    begin
      (* Printf.printf "%8d | " index_cycle; *)
      (* affichage des valeurs des entrees *)
      for i = 0 to pred nb_entrees do
	Printf.printf "%d" (int_of_bool etat_entree.(i));
      done; 
      (* affichage des valeurs des sorties *)
      for i = 0 to pred nb_sorties do
	Printf.printf "%d" (int_of_bool etat_sortie.(i));
      done;
      if not_empty 
      then
	begin
	  Printf.printf "\n";
	  flush stdout
	end
      else ();
      if index_cycle = max_int
      then index_cycle <- 0
      else index_cycle <- succ index_cycle;
    end

  method quit = ()

end
(* ===================================================================== *)

(* ===================================================================== *)
(** Traitement le plus simple pour realiser un traitement plus evolue 
    avec un pipe. Affichage sequentiel ligne par ligne des valeurs, 
    sans aucune presentation. On affiche uniquement les sorties *) 
(* ===================================================================== *)
class traitement_pipe tab_entrees tab_sorties = 
object 
  inherit traitement_seq tab_entrees tab_sorties
  val mutable index_cycle = 0
  val nb_entrees = Array.length tab_entrees 
  val nb_sorties = Array.length tab_sorties
  val nom_entrees = Array.copy tab_entrees
  val nom_sorties = Array.copy tab_sorties
  (* methodes *)

  method get_nom_entrees = nom_entrees

  method get_nom_sorties = nom_sorties

  method init =
    begin
      index_cycle <- 0;
    end

  method traite etat_entree etat_sortie =
    let not_empty = Array.length etat_entree + Array.length etat_sortie > 0 in
    begin
      (* affichage des valeurs des sorties *)
      for i = 0 to pred nb_sorties do
	Printf.printf "%d" (int_of_bool etat_sortie.(i));
      done;
      if not_empty
      then
	begin
	  Printf.printf "\n";
	  flush stdout
	end
      else ();
      if index_cycle = max_int
      then index_cycle <- 0
      else index_cycle <- succ index_cycle;
    end

  method quit = ()

end
(* ===================================================================== *)

(* ===================================================================== *)
(** Traitement pipe non redondant. On affiche le resultat seulement
    s'il a ete modifie. On affiche l'entree et la sortie. *)
(* ===================================================================== *)
class traitement_pipeN tab_entrees tab_sorties = 
object(this)
  inherit traitement_seq tab_entrees tab_sorties
  val mutable index_cycle = 0
  val nb_entrees = Array.length tab_entrees 
  val nb_sorties = Array.length tab_sorties
  val nom_entrees = Array.copy tab_entrees
  val nom_sorties = Array.copy tab_sorties

  val last_sorties = Array.create (Array.length tab_sorties) false

  method get_nom_entrees = nom_entrees

  method get_nom_sorties = nom_sorties

  method init = 
    begin
      index_cycle <- 0;
    end

  method private has_changed etat_sortie = 
	begin
	let ch = ref false in
	for i = 0 to nb_sorties - 1 do
		ch := !ch || ( last_sorties.(i) <> etat_sortie.(i) );
		last_sorties.(i) <- etat_sortie.(i);
	done;
	!ch;
	end

  method traite etat_entree etat_sortie =
    begin
      if ( this#has_changed etat_sortie ) || ( index_cycle = 0 ) 
      then
	begin
	  for i = 0 to pred nb_entrees do
	    Printf.printf "%d" (int_of_bool etat_entree.(i));
	  done; 
	  (* affichage des valeurs des sorties *)
	  for i = 0 to pred nb_sorties do
	    Printf.printf "%d" (int_of_bool etat_sortie.(i));
	  done;
	  Printf.printf "\n";
	  flush stdout;
	end;
      if index_cycle = max_int 
      then index_cycle <- 0
      else index_cycle <- succ index_cycle;

    end

  method quit = () 
end
(* ===================================================================== *)

(* ===================================================================== *)
(**              FICHIER LATEX : TABLEAU DES RESULTATS                   *)
(* ===================================================================== *)
(** filtrer le nom des variables pour garantir la compilation latex *)
let latex_ident_filter s = 
  let q = Queue.create () in
  for i = 0 to pred (String.length s) do
    match s.[i] with
    | '_' -> Queue.push '\\' q; Queue.push '_' q
    | o when List.mem o ['\'';'~'] -> ()
    | e -> Queue.push e q
  done;
  let s' = String.make (Queue.length q) '.' in
  for i = 0 to pred (String.length s') do
    s'.[i] <- Queue.pop q
  done;
  s'

class traitement_latex tab_entrees tab_sorties = 
object 
  inherit traitement_seq tab_entrees tab_sorties
  val mutable index_cycle = 0
  val nb_entrees = Array.length tab_entrees 
  val nb_sorties = Array.length tab_sorties
  val nom_entrees = Array.copy tab_entrees
  val nom_sorties = Array.copy tab_sorties
  (* methodes *)

  method get_nom_entrees = nom_entrees

  method get_nom_sorties = nom_sorties

  method init = 
    let nb_col = nb_entrees + nb_sorties + 1 in
    begin
      index_cycle <- 0;
      Printf.printf "%c Tableau des resultats d'une simulation bopsimu\n" '%';
      Printf.printf "\n\\documentclass[10pt]{article}\n";
      Printf.printf "\\begin{document}\n\n";
      Printf.printf "\\begin{center}\n"; 
      Printf.printf "\\begin{tabular}{\n";
      for i = 1 to pred nb_col do
	Printf.printf "c|";
      done;
      Printf.printf "c}\n";

      Printf.printf "%8s " "Cycle";
      for i = 0 to pred nb_entrees do
	Printf.printf "& %s " (latex_ident_filter nom_entrees.(i));
      done;
      for i = 0 to pred nb_sorties do
	Printf.printf "& %s " (latex_ident_filter nom_sorties.(i));
      done;
      Printf.printf "\\\\ \n";
      flush stdout
    end

  method traite etat_entree etat_sortie =
    begin
      Printf.printf " \\hline";
      Printf.printf "%8d " index_cycle;
      (* affichage des valeurs des entrees *)
      for i = 0 to pred nb_entrees do
	Printf.printf "& %d " (int_of_bool etat_entree.(i));
      done;
      (* affichage des valeurs des sorties *)
      for i = 0 to pred nb_sorties do
	Printf.printf "& %d " (int_of_bool etat_sortie.(i));
      done;
      Printf.printf "\\\\ \n";
      if index_cycle = max_int
      then index_cycle <- 0
      else index_cycle <- succ index_cycle;
      flush stdout;
    end

  method quit = 
    begin
      Printf.printf "\\end{tabular}\n";
      Printf.printf "\\end{center}\n";
      Printf.printf "\\end{document}\n";
      flush stdout;
    end

end
(* ===================================================================== *)

(* ===================================================================== *)
(* ============ APPLICATION GRAPHIQUE : MONTRE DIGITALE ================ *)
(* ============= Exemple d'un traitement plus complexe. ================ *)
(* ==============  Bopsimu est possede par une montre = ================ *)
(* ===================================================================== *)

(* ===================================================================== *)
(** Creer les tableaux des coordonnees des prismes horizontaux *)
(* ===================================================================== *)
let h_tab d x y =
  let u = 4 * d in
  let usur2 = 2 * d in
  let u5 = 5 * u in
  let u6 = 6 * u in
  [|
    (x, y);
    (x + usur2, y + usur2);
    (x + usur2 + u5, y + usur2 );
    (x + u6, y);
    (x + usur2 + u5, y - usur2);
    (x + usur2, y - usur2);
    (x, y)
  |]
(* ===================================================================== *)

(* ===================================================================== *)
(** Creer les tableaux des coordonnees des prismes verticaux (italique : di) *)
(* ===================================================================== *)
let v_tab di d x y =
  let isur2 = 2 * di in
  let u = 4 * d in
  let usur2 = 2 * d in
  let u5 = 5 * u in
  let u6 = 6 * u in
  [|
    (x, y);
    (x - usur2, y + usur2);
    (x - usur2 + isur2, y + usur2 + u5);
    (x + isur2, y + u6);
    (x + usur2 + isur2, y + usur2 + u5);
    (x + usur2, y + usur2);
    (x, y)
  |]
(* ===================================================================== *)

(* ===================================================================== *)
(** Transtypage classique des booleens vers les entiers *)
(* ===================================================================== *)
let char_of_bool = function
  | true -> '1'
  | false -> '0'
(* ===================================================================== *)

(* ===================================================================== *)
(** Le type qui permet de decrire un chiffre du cadran d'affichage *)
(* ===================================================================== *)
type description_chiffre = 
{
  cadre : int * int * int * int;
  coord_prisme : (int * int) array array;
  cache_text : int * int * int * int;
  points_attache : (int * int) array; 
  fils : (int * int) array array;
  text_coord : (int * int) array
}
(* ===================================================================== *)

(* ===================================================================== *)
(** Donner toutes les coordonnees du cadran : voir le papier milimetre 
    original qui est donc en cm  *)
(* ===================================================================== *)
let description_of_param x y = 
  let p = 0.6 in
  let d = 3 in
  let cm = 2 * d in
  let hh = h_tab d and vv = v_tab d d in
  let _prismes = 
    [|
      vv (x + 6 * cm)  (y + 22 * cm + d);
      hh (x + 6 * cm + d) (y + 22 * cm);
      vv (x + 5 * cm) (y + 9 * cm + d);
      hh (x + 5 * cm + d) (y + 9 * cm);
      vv (x + 18 * cm) (y + 9 * cm + d);
      vv (x + 19 * cm) (y + 22 * cm + d);
      hh (x + 7 * cm + d) (y + 35 * cm) 
    |]
  and _points =
    [|
      (x + 5 * cm, y + 23 * cm + d);
      (x + 6 * cm + d, y + 22 * cm);
      (x + 5 * cm, y + 9 * cm + d);
      (x + 11 * cm + d, y + 8 * cm);
      (x + 18 * cm, y + 9 * cm + d);
      (x + 20 * cm, y + 23 * cm + d);
      (x + 19 * cm + d, y + 35 * cm)
    |]
  and _fils = 
    [|
      [|
	(x + 5 * cm, y + 23 * cm + d);
	(x + (int_of_float (33. *. p)), y + 23 * cm + d);
	(x + (int_of_float (16. *. p)), y + 6 * cm + d);
	(x + 5 * cm + d, y + 6 * cm + d);
	(x + 5 * cm + d, y + 5 * cm )	
      |];

      [|
	(x + 6 * cm + d, y + 22 * cm);
	(x + (int_of_float (42. *. p)), y + 22 * cm);
	(x + (int_of_float (26. *. p)), y + 7 * cm);
	(x + 7 * cm + d, y + 7 * cm);
	(x + 7 * cm +d, y + 5 * cm)
      |];

      [|
	(x + 5 * cm, y + 9 * cm + d);
	(x + (int_of_float (47. *. p)), y + 7 * cm + d);
	(x + 9 * cm + d, y + 7 * cm + d);
	(x + 9 * cm + d, y + 5 * cm)
      |];

      [|
	(x + 11 * cm + d, y + 8 * cm);
	(x + 11 * cm + d, y + 5 * cm)
      |];

      [|
	(x + 18 * cm, y + 9 * cm + d);
	(x + (int_of_float (178. *. p)), y + 7 * cm + d);
	(x + 13 * cm + d, y + 7 * cm + d);
	(x + 13 * cm + d, y + 5 * cm)
      |];

      [|
	(x + 20 * cm, y + 23 * cm + d);
	(x + (int_of_float (213. *. p)), y + 23 * cm + d);
	(x + (int_of_float (197. *. p)), y + 7 * cm);
	(x + 15 * cm + d, y + 7 * cm);	
	(x + 15 * cm + d, y + 5 * cm);	
      |];

      [|
	(x + 19 * cm + d, y + 35 * cm);
	(x + (int_of_float (234. *. p)), y + 35 * cm);
	(x + (int_of_float (207. *. p)), y + 6 * cm + d);
	(x + 17 * cm + d, y + 6 * cm + d);
	(x + 17 * cm + d, y + 5 * cm)
      |]

    |]
  and _text =
    let ip = int_of_float p in
    [|
      (x + 5 * cm + ip, y + 2 * cm +d );
      (x + 7 * cm + ip, y + 2 * cm + d );
      (x + 9 * cm + ip, y + 2 * cm + d );
      (x + 11 * cm + ip, y + 2 * cm + d);
      (x + 13 * cm + ip, y + 2 * cm + d);
      (x + 15 * cm + ip, y + 2 * cm + d);
      (x + 17 * cm + ip, y + 2 * cm + d)
    |]
  in
{
  cadre = x, y, 25 * cm, 38 * cm;
  coord_prisme = _prismes;
  (* cache texte : rectangle *)
  cache_text = x + 4 * cm, y + cm, 15 * cm, 3 * cm;
  points_attache = _points;
  (* coordonnees du texte *)
  fils = _fils;
  text_coord = _text
}
(* ===================================================================== *)

(* ===================================================================== *)
(** Le style du cadran : reglage des couleurs de la montre *)
(* ===================================================================== *)
type description_style = 
{
  couleur_fils : color;
  couleur_on : color;
  couleur_off : color;
  couleur_fond : color;
  couleur_cadre : color
}
(* ===================================================================== *)

(* ===================================================================== *)
(** Interface de la classe qui gere les chiffres du cadran graphique *)
(* ===================================================================== *)
class type type_chiffre =
object
  val s : description_style
  val prisme_state :  bool array
  val des : description_chiffre 
  method private refresh_prisme : int -> unit
  method private update_prisme : int -> bool -> unit
  method private plot_string_state : unit
  method init : unit
  method update : bool array -> int -> unit
end
(* ===================================================================== *)

(* ===================================================================== *)
(** Gerer les Objets chiffres du cadran *) 
(* ===================================================================== *)
class chiffre style x y : type_chiffre =
object(this)
 (* val text_size = 16 *)
  val ry = 3
  val s = style;
  val prisme_state = Array.make 7 false
  val des = description_of_param x y

  method private refresh_prisme i = 
    let etat = prisme_state.(i) in
    begin
      if etat then set_color s.couleur_on else set_color s.couleur_off;
      fill_poly des.coord_prisme.(i);
      set_color s.couleur_fils;
      match des.points_attache.(i) with
      | a,b -> fill_circle a b ry
    end

  method private update_prisme i new_val =
    begin
      prisme_state.(i) <- new_val;
      this#refresh_prisme i;
    end

  method private plot_string_state = 
    begin
    (*  set_text_size text_size; *)
      set_color s.couleur_fond;
      (
      match des.cache_text with
	(a,b,l,h) -> 
	  begin
	    fill_rect a b l h;
	  end
      );

      set_color s.couleur_on;
      for i = 0 to 6 do
	match des.text_coord.(i) with
	| a,b -> 
	    begin
	      moveto a b;
              draw_char (char_of_bool prisme_state.(i));
	    end
      done;
    end 


  method init =
    begin
      set_color s.couleur_fils;
      for i = 0 to 6 do
	draw_poly_line des.fils.(i);
	match des.points_attache.(i) with
	| a,b -> fill_circle a b ry
      done;
      for i = 0 to 6 do
	this#refresh_prisme i
      done;

      set_color s.couleur_cadre;
      (
      match des.cadre with
      | a,b,l,h -> draw_rect a b l h
      );
      this#plot_string_state
    end

  method update new_val index =
    let do_string = ref false in
    begin
      for i = 0 to 6 do
	if prisme_state.(i) <> new_val.(index+i)
	then
	  begin
	    do_string := true;
            this#update_prisme i new_val.(index+i);
	  end
	else ()
      done;
      if (!do_string) 
      then
	this#plot_string_state
      else ()
    end
end
(* ===================================================================== *)

(* ===================================================================== *)
(** Proposition de style pour l'affichage : vert sur noir *)
(* ===================================================================== *)
let first_style =  
{
  couleur_fils = red;
  couleur_on = green;
  couleur_off = rgb 0 20 0;
  couleur_fond = black;
  couleur_cadre = yellow
}
(* ===================================================================== *)

(* ===================================================================== *)
(** Classe du cadran : les valeurs entieres sont calculees pour que le 
    cadran soit bien centre et ne deborde pas de l'ecran *)
(* ===================================================================== *)
class montre style =
  let espace = 150 and x = 14 and y = 30 in
object(this)
  val chiffre = Array.init 6 (fun i -> new chiffre style (x + espace * i + (i/2) * (espace/4)) y)

  val carres = [|(x+310,y+140); (x+645,y+140);
		 (x+310,y+110); (x+645,y+110) |]


  method private plot_carres =
    let affiche_carre (a,b) = 
      begin
	set_color style.couleur_on;
	fill_poly [|(a,b);(a+3,b+12);(a+15,b+12);(a+12,b);(a,b)|];
      end
    in Array.iter affiche_carre carres

  method init = 
    begin
      for i = 0 to 5 do
	chiffre.(i)#init
      done;
      this#plot_carres
    end

  method update tab = 
    (* le tableau est donne a l'envers (42 booleens) *)
    begin
      for i = 0 to 5 do
	chiffre.(i)#update tab (7*(5-i))
      done;
    end
end
(* ===================================================================== *)

(* ===================================================================== *)
(** Expancier les arguments vectoriels d'une net list.*)
(* ===================================================================== *)
let string_list pref n = 
  let rec aux accu i =
    if i < 0 
    then accu
    else aux ((Printf.sprintf "%s[%d]" pref i)::accu) (pred i)
  in aux [] (pred n)
(* ===================================================================== *)

(* ===================================================================== *)
(** Expancier tout une liste d'arguments vectoriels d'une net list *)
(* ===================================================================== *)
let rec expand_arg_list = function
    | [] -> []
    | (n, i)::q -> (string_list n i)@(expand_arg_list q)
(* ===================================================================== *)

(* ===================================================================== *)
(** Vous en avez reve ? On l'a fait : la montre digitale graphique *)
(* ===================================================================== *)
class montre_digitale inentrees insorties = 
  let sorties = Array.of_list 
    (expand_arg_list [("s1",7);("s10",7);("m1",7);("m10",7);("h1",7);("h10",7)])
  in
  let verif = 
    if Array.length sorties > Array.length insorties
    then failwith "Traitement Montre Digitale : 42 signaux attendus"
    else ()
  in
object 
  inherit traitement_seq [||] sorties 
  val mutable index_cycle = 0
  val nb_entrees = 0
  val nb_sorties = 42
  val nom_entrees = [||]
  val nom_sorties = (ignore verif; sorties)
  val la_montre = new montre first_style 
  (* methodes *)

  method get_nom_entrees = nom_entrees

  method get_nom_sorties = nom_sorties
    
  method init = 
    begin
      index_cycle <- 0;
      open_graph " 1000x300";
      set_color black;
      fill_rect 0 0 1000 300;
      set_window_title "Bebop Framework : Montre Digitale (Mathieu Barbin, Ocan Sankur)";
      la_montre#init
    end

  method traite etat_entree etat_sortie =
    begin

      la_montre#update etat_sortie;

      if index_cycle = max_int
      then index_cycle <- 0
      else index_cycle <- succ index_cycle;
    end

  method quit =
    begin
      ignore (wait_next_event [Button_down;Key_pressed]);
      close_graph ()
    end
end
(* ===================================================================== *)

(* ===================================================================== *)
(** Aiguillage : instancier la bonne classe, en fonction du type demande
    pour le traitement. *)
(* ===================================================================== *)
let new_traitement_of_type t entrees sorties = 
    match t with
    | Montre   -> new montre_digitale entrees sorties
    | Standard -> new traitement_default entrees sorties
    | Pipe     -> new traitement_pipe entrees sorties
    | PipeIO   -> new traitement_pipeIO entrees sorties
    | PipeN    -> new traitement_pipeN entrees sorties
    | Latex    -> new traitement_latex entrees sorties
(* ===================================================================== *)
